/**
 * File: WebUtil.java
 * User: z3y2
 * Date: 2010-12-4
 * Time: 下午09:02:21
 * Right: (c) z3y2 All Rights Reserved
 */
package com.mypack.web.util;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.mypack.exception.CommonException;
import com.mypack.util.Assert;


/**
 * 
 * 系统 Web 工具类, 负责对常用的一些普通操作进行封装, 简化操作, 同时也增加了性能
 * 
 * @author z3y2
 * 
 */
public abstract class WebUtil {
	private static final Logger logger = Logger.getLogger(WebUtil.class.getName());

	/** Cookie 默认保存7天 */
	private static final int COOKIE_TIME_OUT = 60 * 60 * 24 * 7; // 默认保存7天
	
	/** Cookie 访问路径,表示在当前目录以及子目录可访问到Cookie */
	private static final String COOKIE_PATH = "/";  // 全网可见
	
	/**
	 * 填充对象值
	 * @param <T> 要注入的模型对象类型
	 * @param request 请求参数
	 * @param clazz 类型
	 * @return 对象
	 */
	public static <T> T populateBean(HttpServletRequest request, Class<T> clazz) {
		Map<String, String> parameterMap = getParameterMap(request);
		PropertyDescriptor[] pds;
		
		try {
			pds = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
		} catch (IntrospectionException e) {
			logger.log(Level.WARNING, e.getMessage(), e);
			throw new CommonException(e.getMessage(), e);
		}
		
		T bean;
		try {
			bean = clazz.newInstance();
		} catch (InstantiationException e) {
			logger.log(Level.WARNING, e.getMessage(), e);
			throw new CommonException(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			logger.log(Level.WARNING, e.getMessage(), e);
			throw new CommonException(e.getMessage(), e);
		}
		
		for (PropertyDescriptor pd : pds) {
			Method writer = pd.getWriteMethod();
			if (writer != null) {
				try {
					String value = parameterMap.get(pd.getName());
					writer.invoke(bean, convert(pd.getPropertyType(), value));
				} catch (Exception e) {
					logger.warning(clazz + "'s property " + pd.getName() + " is setted failed, type is " + pd.getPropertyType());
				}
			}
		}
		
		return bean;
	}
	
	
	private static Object convert(Class clazz, String string) {
		if (clazz == String.class) {
			return string;
		}
		if (clazz == int.class || clazz == Integer.class) {
			return Integer.valueOf(string);
		}
		if (clazz == boolean.class || clazz == Boolean.class) {
			return Boolean.valueOf(string);
		}
		if (clazz == float.class || clazz == Float.class) {
			return Float.valueOf(string);
		}
		if (clazz == double.class || clazz == Double.class) {
			return Double.valueOf(string);
		}
		if (clazz == byte.class || clazz == Byte.class) {
			return Byte.valueOf(string);
		}
		if (clazz == char.class || clazz == Character.class) {
			return Character.valueOf(string.charAt(0));
		}
		if (clazz == short.class || clazz == Short.class) {
			return Short.valueOf(string);
		}
		if (clazz == long.class || clazz == Long.class) {
			return Long.valueOf(string);
		}
		if (clazz == Date.class) {
			SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			try {
				return f.parse(string);
			} catch (ParseException e) {
				f = new SimpleDateFormat("yyyy-MM-dd");
				try {
					return f.parse(string);
				} catch (ParseException e1) {
					f = new SimpleDateFormat("yyyy-MM-dd HH:mm");
					try {
						return f.parse(string);
					} catch (ParseException e2) {
						logger.info("parse date error, date = " + string);
					}
				}
			}
		}
		return null;
	}
	
	public static void forward(HttpServletRequest request, HttpServletResponse response, String path) 
			throws ServletException, IOException {
		request.getRequestDispatcher(path).forward(request, response);
	}
	
	/**
	 * 从相对路径得到绝对路径
	 * @param servletContext the servlet context of the web application
	 * @param path the path within the web application
	 * @return the corresponding real path
	 */
	public static String getRealPath(ServletContext servletContext, String path) throws FileNotFoundException {
		Assert.notNull(servletContext, "ServletContext must not be null");
		// Interpret location as relative to the web application root directory.
		if (!path.startsWith("/")) {
			path = "/" + path;
		}
		String realPath = servletContext.getRealPath(path);
		if (realPath == null) {
			throw new FileNotFoundException(
					"ServletContext resource [" + path + "] cannot be resolved to absolute file path - " +
					"web application archive not expanded?");
		}
		return realPath;
	}


	/**
	 * 得到当前会话编号 
	 * @param request current HTTP request
	 * @return the session id, or <code>null</code> if none
	 */
	public static String getSessionId(HttpServletRequest request) {
		Assert.notNull(request, "Request must not be null");
		HttpSession session = request.getSession(false);
		return (session != null ? session.getId() : null);
	}

	/**
	 * 从当前会话中查询值,如果不存在不要新建一个会话,优化过的方法,提高性能
	 * @param request current HTTP request
	 * @param name the name of the session attribute
	 * @return the value of the session attribute, or <code>null</code> if not found
	 */
	public static Object getSessionAttribute(HttpServletRequest request, String name) {
		Assert.notNull(request, "Request must not be null");
		HttpSession session = request.getSession(false);
		return (session != null ? session.getAttribute(name) : null);
	}

	/**
	 * 从当前会话查询值,如果查不到则抛出异常
	 * @param request current HTTP request
	 * @param name the name of the session attribute
	 * @return the value of the session attribute, or <code>null</code> if not found
	 */
	public static Object getRequiredSessionAttribute(HttpServletRequest request, String name)
	    throws IllegalStateException {

		Object attr = getSessionAttribute(request, name);
		if (attr == null) {
			throw new IllegalStateException("No session attribute '" + name + "' found");
		}
		return attr;
	}
	
	
	/**
	 * 从当前路径得到文件名,工具方法, 方便调用
	 * Correctly resolves nested paths such as "/products/view.html" as well.
	 * @param urlPath the request URL path (e.g. "/index.html")
	 * @return the extracted URI filename (e.g. "index")
	 */
	public static String extractFilenameFromUrlPath(String urlPath) {
		int end = urlPath.indexOf(';');
		if (end == -1) {
			end = urlPath.indexOf('?');
			if (end == -1) {
				end = urlPath.length();
			}
		}
		int begin = urlPath.lastIndexOf('/', end) + 1;
		String filename = urlPath.substring(begin, end);
		int dotIndex = filename.lastIndexOf('.');
		if (dotIndex != -1) {
			filename = filename.substring(0, dotIndex);
		}
		return filename;
	}
	
	/**
	 * 返回当前请求中的参数的键值映射,比传统的更好用,将非数组转成原始值,而不再是数组
	 * @param request HTTP request in which to look for parameters
	 * @return map containing request parameters <b>without the prefix</b>,
	 */
	public static Map<String, String> getParameterMap(ServletRequest request) {
		return getParametersStartingWith(request, "");
	}
	
	/**
	 * 返回当前请求中的参数的键值映射,比传统的更好用,将非数组转成原始值,而不再是数组
	 * @param request HTTP request in which to look for parameters
	 * @param prefix the beginning of parameter names
	 * @return map containing request parameters <b>without the prefix</b>,
	 */
	public static Map<String, String> getParametersStartingWith(ServletRequest request, String prefix) {
		Assert.notNull(request, "Request must not be null");
		Enumeration paramNames = request.getParameterNames();
		Map params = new TreeMap();
		if (prefix == null) {
			prefix = "";
		}
		while (paramNames != null && paramNames.hasMoreElements()) {
			String paramName = (String) paramNames.nextElement();
			if ("".equals(prefix) || paramName.startsWith(prefix)) {
				String unprefixed = paramName.substring(prefix.length());
				String[] values = request.getParameterValues(paramName);
				if (values == null || values.length == 0) {
					// Do nothing, no values found at all.
				}
				else if (values.length > 1) {
					params.put(unprefixed, values);
				}
				else {
					params.put(unprefixed, values[0]);
				}
			}
		}
		return params;
	}
	
	
	/**
	 * 设置当前会话中的值,如果是空的话,则不处理并且从会话中移除僵尸引用
	 * @param request current HTTP request
	 * @param name the name of the session attribute
	 * @param value the value of the session attribute
	 */
	public static void setSessionAttribute(HttpServletRequest request, String name, Object value) {
		Assert.notNull(request, "Request must not be null");
		if (value != null) {
			request.getSession().setAttribute(name, value);
		}
		else {
			HttpSession session = request.getSession(false);
			if (session != null) {
				session.removeAttribute(name);
			}
		}
	}
	
	
	/** 
	 * 查询所有的cookie, 返回键值形式,方便客户端调用
	 * @param request HttpServletRequest
	 * @return Map 不可变集合
	 */
	public static Map<String, String> getCookies(HttpServletRequest request) {
		Cookie[] cookies = request.getCookies();
		
		if (cookies == null) {
			return Collections.EMPTY_MAP; // 返回空集合,比返回 null 更安全
		}
		
		Map<String, String> map = new HashMap<String, String>(cookies.length);
		
		for (Cookie cookie : cookies) {
			map.put(cookie.getName(), cookie.getValue());
		}
		
		return Collections.unmodifiableMap(map); // 不可变
	}
	
	/** 
	 * 根据 cookie 的名字返回 cookie 的值
	 * @param request
	 * @param name
	 * @return 
	 */
	public static String getCookieValue(HttpServletRequest request, String name) {
		return getCookies(request).get(name);
	}
	
	/** 
	 * 删除Cookie
	 * @param resp
	 * @param name 
	 */
	public static void removeCookie(HttpServletResponse response, String name) {
		Cookie cookie = new Cookie(name, "");
		cookie.setMaxAge(0);
		cookie.setPath(COOKIE_PATH);
		response.addCookie(cookie);
	}
	
	/** 
	 * 修改Cookie
	 * @param response
	 * @param name
	 * @param value 
	 */
	public static void modifyCookie(HttpServletResponse response, String name, String value) {
		addCookie(response, name, value);
	}
	
	/** 
	 * 增加Cookies
	 * @param cookies map
	 */
	public static void addCookies(HttpServletResponse response, Map<String, String> cookies) {
		
		Assert.notEmpty(cookies); // cookie 不能为空

		for (Map.Entry<String, String> each : cookies.entrySet()) {
			Cookie cookie = new Cookie(each.getKey(), each.getValue());
			cookie.setMaxAge(COOKIE_TIME_OUT); // 默认保存7天
			cookie.setPath(COOKIE_PATH); // 全网可见
			response.addCookie(cookie);
		}
	}
	
	/** 
	 * 增加Cookie
	 * @param response
	 * @param name
	 * @param value cookie 的值
	 */
	public static void addCookie(HttpServletResponse response, String name, String value) {
		Cookie cookie = new Cookie(name, value);
		cookie.setMaxAge(COOKIE_TIME_OUT); // 默认保存7天
		cookie.setPath(COOKIE_PATH); // 全网可见
		response.addCookie(cookie);
	}

}
